const http = require('http');
const crypto = require('crypto');
const { exec } = require('child_process');
const path = require('path');
const { sendDeployNotification, sendDeployStartNotification } = require('./mail-service');

// 加载配置
const config = require('./config-loader');

// 验证 webhook 签名
function verifySignature(timestamp, token) {
    if (!token || !timestamp) {
        console.log('缺少必要的签名参数');
        return false;
    }

    try {
        // 1. 构造签名字符串
        const stringToSign = `${timestamp}\n${config.server.secret}`;

        // 2. 使用 UTF-8 编码获取字节数组
        const stringToSignBytes = Buffer.from(stringToSign, 'utf8');
        const secretBytes = Buffer.from(config.server.secret, 'utf8');

        // 3. 创建 HmacSHA256 实例并初始化
        const hmac = crypto.createHmac('sha256', secretBytes);

        // 4. 计算签名
        const signData = hmac.update(stringToSignBytes).digest();

        // 5. Base64 编码
        const base64Signature = signData.toString('base64');

        return token === base64Signature;
    } catch (error) {
        console.error('签名验证过程出错：', error);
        return false;
    }
}

// 执行 Git 命令
async function executeGitCommand(command, repoPath) {
    return new Promise((resolve, reject) => {
        console.log(`执行命令: ${command} (在 ${repoPath})`);
        console.log("%c Line:62 🍌 command", "color:#ed9ec7", repoPath, ">", command);
        exec(command, { cwd: repoPath }, (error, stdout, stderr) => {
            const output = stdout + (stderr ? `\nError: ${stderr}` : '');
            if (error) {
                console.error(`执行命令出错: ${error}`);
                reject({ error, output });
                return;
            }

            console.log("%c Line:67 🥃", "color:#4fff4B", stdout);
            resolve({ success: true, output });
        });
    });
}

// 执行自定义脚本
async function executeCustomScript(scriptPath) {
    return new Promise((resolve, reject) => {
        console.log(`执行脚本: ${scriptPath}`);
        exec(`bash ${scriptPath}`, { cwd: path.dirname(scriptPath) }, (error, stdout, stderr) => {
            const output = stdout + (stderr ? `\nError: ${stderr}` : '');
            if (error) {
                console.error(`执行脚本出错: ${error}`);
                reject({ error, output });
                return;
            }
            console.log(`脚本输出: ${stdout}`);
            resolve({ success: true, output });
        });
    });
}

/**
 * 在后台继续执行部署流程
 * @param {Object} options - 部署选项
 * @param {string} options.projectFullName - 项目全名
 * @param {string} options.branchName - 分支名称
 * @param {string} options.scriptPathToExecute - 要执行的脚本路径
 * @param {boolean} options.deploySuccess - 部署是否成功
 * @param {Array<string>} options.deployLogs - 部署日志
 * @param {Function} options.addDeployLog - 添加部署日志的函数
 * @returns {Promise<void>}
 */
async function continueDeploymentInBackground(options) {
    // 参数验证
    if (!options || typeof options !== 'object') {
        throw new Error('部署选项必须是一个对象');
    }

    const {
        projectFullName,
        branchName,
        scriptPathToExecute,
        deploySuccess: initialDeploySuccess,
        deployLogs,
        addDeployLog
    } = options;

    // 验证必要参数
    if (!projectFullName) {
        throw new Error('缺少项目全名参数');
    }
    if (!branchName) {
        throw new Error('缺少分支名称参数');
    }
    if (!Array.isArray(deployLogs)) {
        throw new Error('部署日志必须是一个数组');
    }
    if (typeof addDeployLog !== 'function') {
        throw new Error('addDeployLog必须是一个函数');
    }

    // 使用局部变量跟踪部署状态，避免修改传入的参数
    let deploySuccess = initialDeploySuccess;

    try {
        // 如果Git操作成功，执行自定义脚本
        if (deploySuccess && scriptPathToExecute) {
            // 发送部署开始通知
            console.log("开始发送部署开始通知");
            try {
                await sendDeployStartNotification(
                    projectFullName,
                    branchName
                );
            } catch (notifyError) {
                console.error('发送部署开始通知失败:', notifyError);
                // 继续执行，不影响部署流程
            }

            try {
                const scriptResult = await executeCustomScript(scriptPathToExecute);
                addDeployLog(`\n-----------部署脚本执行结果: ------------\n${scriptResult.output}\n-----------------------\n`);
            } catch (scriptError) {
                deploySuccess = false;
                addDeployLog(`\n-----------部署脚本执行失败: ------------\n${scriptError.output || scriptError.message}\n-----------------------\n`);
            }
        }

        console.log("开始发送邮件通知");
        // 发送邮件通知
        try {
            const sendRes = await sendDeployNotification(
                projectFullName,
                branchName,
                deploySuccess,
                deployLogs.join('\n\n')
            );
            console.log("发送邮件通知结果:", sendRes);
        } catch (mailError) {
            console.error('发送邮件通知失败:', mailError);
        }

        console.log(deploySuccess ? "部署成功完成" : "部署失败");
    } catch (backgroundError) {
        console.error('后台部署过程中出错:', backgroundError);
        try {
            addDeployLog(`后台部署过程中出现未处理的错误: ${backgroundError.message || backgroundError}`);
            await sendDeployNotification(
                projectFullName,
                branchName,
                false,
                deployLogs.join('\n\n')
            );
        } catch (mailError) {
            console.error('发送失败通知邮件时出错:', mailError);
        }
    }
}

// 处理 webhook 请求
async function handleWebhook(req, res) {
    let body = '';
    req.on('data', chunk => {
        body += chunk.toString();
    });

    req.on('end', async () => {
        try {


            // 获取 Gitee 的签名信息
            const token = req.headers['x-gitee-token'];
            const timestamp = req.headers['x-gitee-timestamp'];

            // 验证签名
            if (!verifySignature(timestamp, token)) {
                console.error('签名验证失败');
                res.statusCode = 401;
                res.end('签名验证失败');
                return;
            }

            const payload = JSON.parse(body);

            // 获取仓库配置
            const repoConfig = config.repositories[payload.project.full_name];
            if (!repoConfig) {
                const result = `未找到仓库配置: ${payload.project.full_name}`
                console.error(result);
                res.statusCode = 404;
                res.end(result);
                return;
            }

            // 检查是否是目标分支的推送
            if (payload.ref === `refs/heads/${repoConfig.branch}`) {
                console.log(`检测到 ${repoConfig.branch} 分支的更新`);

                // 收集部署日志
                let deployLogs = [];
                const addDeployLog = (message) => {
                    console.log("%c Line:142 🍔 message", "color:#ea7e5c", message);
                    deployLogs.push(`[${new Date().toLocaleString()}] ${message}`);
                }
                let deploySuccess = true;

                addDeployLog(`检测到 ${repoConfig.branch} 分支的更新`);

                try {
                    // 拉取最新代码
                    try {
                        const fetchResult = await executeGitCommand('git fetch origin', repoConfig.repoPath);
                        addDeployLog(`\n-----------Git fetch 结果:------------\n${fetchResult.output}\n-----------------------\n$`);

                        const resetResult = await executeGitCommand(`git reset --hard origin/${repoConfig.branch}`, repoConfig.repoPath);
                        addDeployLog(`\n-----------Git reset 结果:------------\n${resetResult.output}\n-----------------------\n`);
                    } catch (gitError) {
                        deploySuccess = false;
                        addDeployLog(`\n-----------Git 操作失败: ------------\n${gitError.output || gitError.message}\n-----------------------\n`);
                    }

                    // 先返回响应，让请求不阻塞
                    res.statusCode = 200;
                    res.end('请求已接收，部署过程将在后台继续执行');
                    console.log("请求已返回，部署过程将在后台继续执行");

                    // 调用后台部署函数，传入所有需要的参数作为一个对象
                    // 这个函数会在响应返回后继续执行
                    continueDeploymentInBackground({
                        projectFullName: payload.project.full_name,
                        branchName: repoConfig.branch,
                        scriptPathToExecute: repoConfig.scriptPath,
                        deploySuccess,
                        deployLogs,
                        addDeployLog
                    }).catch(error => {
                        console.error('后台部署函数执行出错:', error);
                    });
                } catch (error) {
                    console.error('部署初始阶段出错:', error);

                    // 返回响应
                    res.statusCode = 500;
                    res.end('部署初始阶段出现错误');

                    // 尝试发送失败通知
                    try {
                        addDeployLog(`部署初始阶段出现未处理的错误: ${error.message || error}`);
                        await sendDeployNotification(
                            payload.project.full_name,
                            repoConfig.branch,
                            false,
                            deployLogs.join('\n\n')
                        );
                    } catch (mailError) {
                        console.error('发送失败通知邮件时出错:', mailError);
                    }
                }
            } else {
                console.log("%c Line:152 🥚 非目标分支更新，忽略", "color:#2eafb0");
                res.statusCode = 200;
                res.end('非目标分支更新，忽略');
            }
        } catch (error) {
            console.error('处理 webhook 时出错:', error);
            res.statusCode = 500;
            res.end('服务器内部错误');
        }
    });
}

// 创建服务器
const server = http.createServer(handleWebhook);

// 启动服务器
server.listen(config.server.port, () => {
    console.log(`Webhook 服务器运行在端口 ${config.server.port}`);
    console.log('已配置的仓库：');
    Object.keys(config.repositories).forEach(repo => {
        console.log(`- ${repo}`);
    });
}).on('error', (err) => {
    console.error('服务器启动失败:', err);
    setTimeout(() => {
        console.log('3秒后退出');

        process.exit(1);
    }, 3000);
})
